#ifndef CRUDE_HANDLER_MGR_H_
#define CRUDE_HANDLER_MGR_H_
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>

namespace crude {
template <class BaseType>
class HandlerMgr {
  typedef std::vector<BaseType *> _Vector;
  typedef std::shared_ptr<_Vector> _VectorPtr;
  typedef std::map<std::string, _VectorPtr> _Map;
  typedef std::shared_ptr<_Map> _MapPtr;
  ;

  template <class _Ty>
  _VectorPtr GetVector(bool create) {
    if (!map_ && !create) return nullptr;
    if (!map_ && create) map_ = std::make_shared<_Map>();
    std::string name = typeid(_Ty).name();
    auto it = map_->find(name);
    if (it != map_->end()) {
      return it->second;
    } else if (create) {
      (*map_)[name] = std::make_shared<_Vector>();
      return (*map_)[name];
    }
    return nullptr;
  }

  _MapPtr map_;

 public:
  template <class _Ty>
  bool Register(_Ty *handler) {
    if (!handler) {
      return false;
    }
    auto Vector = GetVector<_Ty>(true);
    if (!HasHandle(handler)) Vector->push_back(handler);
    return true;
  }

  template <class _Ty>
  void Unregister(_Ty *handler) {
    if (!handler) return;
    auto HandlerList = GetVector<_Ty>(false);
    if (!HandlerList) return;

    for (auto it = HandlerList->begin(); it != HandlerList->end(); it++) {
      if (*it == handler) {
        HandlerList->erase(it, it + 1);
        if (HandlerList.size() == 0) map_->erase(typeid(_Ty).name());
        return;
      }
    }
  }

  template <class _Ty>
  void UnregisterAll() {
    auto HandlerList = GetVector<_Ty>(false);
    if (!HandlerList) return;
    map_->erase(typeid(_Ty).name());
  }

  template <class _Ty>
  bool HasHandle(_Ty *handler) {
    auto Vector = GetVector<_Ty>(false);
    if (!Vector) return false;
    for (auto it = Vector->begin(); it != Vector->end(); it++) {
      if (*it == handler) return true;
    }
    return false;
  }

  template <class _Ty, class _Fn>
  void AllExec(_Fn fn) {
    auto Vector = GetVector<_Ty>(false);
    if (!Vector) return;
    for (auto it = Vector->begin(); it != Vector->end(); it++) {
      if (*it) fn(reinterpret_cast<_Ty *>(*it));
    }
  }

  template <class _Ty, class _Fn>
  bool EitherExecSuccess(_Fn fn) {
    auto Vector = GetVector<_Ty>(false);
    if (!Vector) return false;
    for (auto it = Vector->begin(); it != Vector->end(); it++) {
      if (*it) {
        if (fn(reinterpret_cast<_Ty *>(*it))) return true;
      }
    }
    return false;
  }

  template <class _Ty, class _Fn>
  bool AllExecSuccess(_Fn fn) {
    auto Vector = GetVector<_Ty>(false);
    if (!Vector) return false;
    for (auto it = Vector->begin(); it != Vector->end(); it++) {
      if (*it) {
        if (!fn(reinterpret_cast<_Ty *>(*it))) return false;
      }
    }
    return true;
  }

  friend std::ostream &operator<<(std::ostream &os,
                                  const HandlerMgr<BaseType> &mgr) {
    if (!mgr.map_) {
      os << "Empty! \n";
      return os;
    }
    os << "Handler Map Size : " << mgr.map_->size() << "\n";
    for (auto it = mgr.map_->begin(); it != mgr.map_->end(); it++) {
      auto Vector = it->second;
      os << " Vector Type : " << it->first.c_str()
         << " , Vector Size: " << (Vector ? Vector->size() : 0) << "\n";
      if (Vector) {
        for (auto it_v = Vector->begin(); it_v != Vector->end(); it_v++) {
          os << "    Handler : " << std::hex << *it_v << "\n";
        }
      }
    }
    return os;
  }
};
}  // namespace crude
#endif
